import { useCallback, useEffect, useState } from "react";

/**
 * useRuntimeManifest is a custom hook that fetches the runtime manifest from the NiFi API.
 *
 * @returns
 */
export function useRuntimeManifest() {
  const [runtimeManifest, setRuntimeManifest] = useState({
    runtimeManifest: { bundles: [] },
  });

  useEffect(() => {
    (async () => {
      const resp = await fetch("/nifi-api/flow/runtime-manifest");
      const json = await resp.json();
      setRuntimeManifest(json);
    })();
  }, []);

  return runtimeManifest;
}

/**
 * getGroup fetches the Process Group with the specified ID.
 *
 * @param id
 * @returns
 */
export async function getGroup(id: string) {
  const resp = await fetch(`/nifi-api/process-groups/${id}`);
  return await resp.json();
}

/**
 * getChildGroups fetches the child groups of the specified parent group.
 *
 * @param parentGroupId
 * @returns
 */
export async function getChildGroups(parentGroupId: string) {
  const resp = await fetch(
    `/nifi-api/flow/process-groups/${parentGroupId}?uiOnly=true`
  );
  const {
    processGroupFlow: {
      flow: { processGroups },
    },
  } = await resp.json();
  return processGroups;
}

/**
 * createGroup creates a new Process Group.
 *
 * @param parentGroup
 * @param name
 * @param comments
 * @returns
 */
export async function createGroup(
  parentGroup: any,
  { name, comments }: { name: string; comments: string }
) {
  const data = {
    revision: {
      clientId: parentGroup.revision.clientId,
      version: 0,
    },
    disconnectedNodeAcknowledged: false,
    component: {
      position: { x: 0, y: 0 },
      name,
      comments,
    },
  };

  const resp = await fetch(
    `/nifi-api/process-groups/${parentGroup.id}/process-groups`,
    {
      method: "POST",
      body: JSON.stringify(data),
      headers: { "Content-Type": "application/json" },
    }
  );

  return await resp.json();
}

/**
 * updateGroup updates the specified Process Group.
 *
 * @param group
 * @param name
 * @param comments
 * @returns
 */
export async function updateGroup(
  group: any,
  { name, comments }: { name: string; comments: string }
) {
  const {
    component: {
      id,
      defaultBackPressureDataSizeThreshold,
      defaultBackPressureObjectThreshold,
      defaultFlowFileExpiration,
      executionEngine,
      flowfileConcurrency,
      flowfileOutboundPolicy,
      logFileSuffix,
      parameterContext,
    },
  } = group;
  const data = {
    disconnectedNodeAcknowledged: false,
    processGroupUpdateStrategy: "DIRECT_CHILDREN",
    revision: group.revision,
    component: {
      id,
      name,
      comments,
      defaultBackPressureDataSizeThreshold,
      defaultBackPressureObjectThreshold,
      defaultFlowFileExpiration,
      executionEngine,
      flowfileConcurrency,
      flowfileOutboundPolicy,
      logFileSuffix,
      parameterContext,
    },
  };
  const resp = await fetch(`/nifi-api/process-groups/${id}`, {
    method: "PUT",
    body: JSON.stringify(data),
    headers: { "Content-Type": "application/json" },
  });
  return await resp.json();
}

/**
 * deleteGroup deletes the specified Process Group.
 *
 * @param group
 * @returns
 */
export async function deleteGroup(group: any) {
  const {
    id,
    revision: { clientId, version },
  } = group;
  const resp = await fetch(
    `/nifi-api/process-groups/${id}?clientId=${clientId}&version=${version}&disconnectedNodeAcknowledged=false`,
    {
      method: "DELETE",
    }
  );

  return await resp.json();
}

/**
 * uploadGroup creates a new Process Group templated with the uploaded process group.
 *
 * @param parentGroup
 * @param groupName
 * @param templateContent
 * @returns
 */
export async function uploadGroup(
  parentGroup: any,
  groupName: string,
  templateContent: string
) {
  const data = new FormData();
  data.set("id", parentGroup.id);
  data.set("groupName", groupName);
  data.set("positionX", "0");
  data.set("positionY", "0");
  data.set("clientId", parentGroup.revision.clientId);
  data.set("disconnectedNodeAcknowledged", "false");
  data.set("file", new Blob([templateContent]), "template");

  const resp = await fetch(
    `/nifi-api/process-groups/${parentGroup.id}/process-groups/upload`,
    {
      method: "POST",
      body: data,
    }
  );
  const json = await resp.json();
  return json;
}

/**
 * findChildGroup finds the child group with the specified name.
 *
 * @param parentGroupId
 * @param name
 * @returns
 */
export async function findChildGroup(parentGroupId: string, name: string) {
  const groups = await getChildGroups(parentGroupId);
  for (const group of groups) {
    if (group.component.name === name) {
      return group;
    }
  }

  throw new Error(`没有找到名为 ${name} 的 ProcessorGroup。`);
}

export function useTaskRoot() {
  const [taskRoot, setTaskRoot] = useState<any>();

  const updateTaskRoot = useCallback(async () => {
    const nifiRoot = await getGroup("root");
    try {
      setTaskRoot(await findChildGroup(nifiRoot.id, "任务管理系统"));
    } catch (e) {
      // Create the task root group if it does not exist
      // setTaskRoot(await createGroup(nifiRoot, {
      //   name: "任务管理系统",
      //   comments: "所有任务管理的根处理组",
      // }));
      console.error(e);
      // Retry after 3 second
      setTimeout(updateTaskRoot, 3000);
    }
  }, []);

  useEffect(() => {
    updateTaskRoot();
  }, [updateTaskRoot]);

  return taskRoot;
}

/**
 * getTaskCategoryList fetches the list of task categories.
 *
 * @param taskRoot
 * @returns
 */
export async function getTaskCategoryList(taskRoot: any) {
  return taskRoot ? await getChildGroups(taskRoot.id) : [];
}

/**
 * getTaskList fetches the list of tasks.
 *
 * @param taskRoot
 * @returns
 */
export async function getTaskList(taskRoot: any) {
  const categoryList: any[] = await getTaskCategoryList(taskRoot);
  let taskList: any[] = [];
  for (const category of categoryList) {
    const taskListOfCurrentCategory: any[] = await getChildGroups(category.id);
    for (const task of taskListOfCurrentCategory) {
      task.parentGroup = category;
    }
    taskList = [...taskList, ...taskListOfCurrentCategory];
  }
  return taskList;
}
